﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Data;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Configuration;

namespace GLSimulationControl
{
    public partial class SimulatorControlGL : UserControl
    {
        private PinnedObject<IntPtr> _hdc;
        private PinnedObject<IntPtr> _hglrc;
        private PinnedObject<IntPtr> _library;
        private Point lastMousePoint;

        public bool IsMouseDown { get; set; }

        public bool TrackballEnabled
        {
            get;
            set;
        }

        internal IntPtr HDC
        {
            get
            {
                return this._hdc.ManangedObject;
            }
        }

        internal IntPtr HGLRC
        {
            get
            {
                return this._hglrc.ManangedObject;
            }
        }

        public delegate void GetStatesProcessProgress(int value);
        public delegate void GetErrorNotify(string errName);
        #region Kernel
        [DllImport( "kernel32.dll" )]
        internal static extern IntPtr LoadLibrary(String filename);

        [DllImport( "kernel32.dll" )]
        internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);

        [DllImport( "kernel32.dll" )]
        internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

        [DllImport( "kernel32.dll" )]
        internal static extern bool FreeLibrary(IntPtr hModule);
        internal void GLFreeLibrary(IntPtr hModule)
        {
            FreeLibrary( hModule );
        }

        [DllImport( "kernel32.dll", SetLastError = true )]
        static extern bool SetDllDirectory(string lpPathName);

        #endregion

        #region WindowsGraphics
        [DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern IntPtr GetDC(IntPtr hWnd);

        [DllImport( "gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern int ChoosePixelFormat(IntPtr hdc, [In] ref PIXELFORMATDESCRIPTOR ppfd);

        [DllImport( "gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern bool SetPixelFormat(IntPtr hdc, int iPixelFormat, ref PIXELFORMATDESCRIPTOR ppfd);

        [DllImport( "opengl32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern IntPtr wglCreateContext(IntPtr hDC);

        [DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

        [DllImport( "glew32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        static extern int glewInit();
        #endregion

        [DllImport( "opengl32.dll", EntryPoint = "glGetString", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        static extern IntPtr _glGetString(StringName name);
        public static string glGetString(StringName name)
        {
            return Marshal.PtrToStringAnsi( _glGetString( name ) );
        }

        #region GraphicsAddFunctions
        [DllImport( "gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern bool SwapBuffers(IntPtr hdc);
        #endregion

        #region 3CSimulatorFunctions
        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void Test();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void Reshape(int w, int h);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        internal static extern void ZoomIn();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        internal static extern void ZoomOut();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void Render();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void InitGL(IntPtr hdc, IntPtr hglrc);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void DeleteSimulation();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void CreateSimulation(uint width, uint height, uint depth);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void CreateMaterial(uint width, uint height, uint depth);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern bool PerformAnimationStep(GetStatesProcessProgress progressNotifyFunc,GetErrorNotify errorNotifyFunc);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void RewindAnimation();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void ProcessStates(GetStatesProcessProgress getStates,GetErrorNotify errorNotify);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void LoadPathFile([MarshalAs( UnmanagedType.LPStr )] string fileName, GetStatesProcessProgress progressFuncNotify, GetErrorNotify errorFuncNotify);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void RotateScene(float oldX, float oldY, float newX, float newY, float maxRadius);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetMaterialWidth();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetMaterialHeight();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetMaterialDepth();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetPathOriginX();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetPathOriginY();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetPathOriginZ();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void SetPathOriginX(double val);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void SetPathOriginY(double val);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void SetPathOriginZ(double val);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetToolX();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetToolY();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetToolZ();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void SetToolX(double value);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void SetToolY(double value);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void SetToolZ(double value);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void CreateBallMillTool(double length, int diameter);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void CreateToroidalTool(double length, int diameter);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void TranslateLeft();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void TranslateRight();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void TranslateUp();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void TranslateDown();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetCameraPosX();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetCameraPosY();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetCameraPosZ();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double SetCameraPosX(double val);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double SetCameraPosY(double val);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double SetCameraPosZ(double val);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double GetFOV();

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern double SetFOV(double val);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern void SetPathVisualizationVisible(bool on);

        [DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        public static extern uint GetCurrentInstructionNumber();

        //[DllImport( "3CSimulatorLib.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true )]
        //public static extern void GenerateAndSavePathFile([MarshalAs( UnmanagedType.LPStr )] string fileName);

        #endregion

        [SecurityPermissionAttribute( SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode )]
        public SimulatorControlGL()
        {
            InitializeComponent();
            this.SetStyle( ControlStyles.EnableNotifyMessage, true );
            this.IsMouseDown = false;
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
                return;
            try
            {
                Configuration config = ConfigurationManager.OpenExeConfiguration( ConfigurationUserLevel.None );
                SetDllDirectory( config.AppSettings.Settings["DllDirectory"].Value );
                glGetString( 0 );
                this._library = new PinnedObject<IntPtr>();
                this._hdc = new PinnedObject<IntPtr>();
                this._hglrc = new PinnedObject<IntPtr>();
                this.Func_Init( this.Handle );
                int err = glewInit();
                InitGL( this.HDC, this.HGLRC );
                CreateSimulation( 150, 150, 50 );
                SimulatorControlGL_Paint( null, null );
                //GenerateAndSavePathFile( "bSpline.txt" );
            }
            catch (Exception e)
            {
                MessageBox.Show( e.Message );
            }
        }

        public void Func_Init(IntPtr hwnd)
        {
            this._hdc.ManangedObject = GetDC( hwnd );
            var pixelFormatDescriptor = new PIXELFORMATDESCRIPTOR();
            pixelFormatDescriptor.Init();

            var pixelFormat = ChoosePixelFormat( _hdc.ManangedObject, ref pixelFormatDescriptor );
            if (!SetPixelFormat( _hdc.ManangedObject, pixelFormat, ref pixelFormatDescriptor ))
                throw new Win32Exception( Marshal.GetLastWin32Error() );
            if ((this._hglrc.ManangedObject = wglCreateContext( _hdc.ManangedObject )) == IntPtr.Zero)
                throw new Win32Exception( Marshal.GetLastWin32Error() );
        }

        private void SimulatorControlGL_Paint(object sender, PaintEventArgs e)
        {
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime || this.DesignMode)
                return;
            Render();
            SwapBuffers( this.HDC );
        }

        [System.Security.Permissions.PermissionSet( System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust" )]
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0x14)//filter out the WM_ERASEBACKGROUND
            {
                return;
            }
            base.WndProc( ref m );
        }

        private void SimulatorControlGL_Resize(object sender, EventArgs e)
        {
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime || this.DesignMode)
                return;
            Reshape( this.Width, this.Height );
            this.Refresh();
        }

        private void SimulatorControlGL_MouseDown(object sender, MouseEventArgs e)
        {
            this.lastMousePoint = e.Location;
            this.IsMouseDown = true;
        }

        private void SimulatorControlGL_MouseUp(object sender, MouseEventArgs e)
        {
            this.lastMousePoint = e.Location;
            this.IsMouseDown = false;
        }

        private void SimulatorControlGL_MouseMove(object sender, MouseEventArgs e)
        {
            if (!this.TrackballEnabled || !this.IsMouseDown)
                return;
            //float lastScaledX = this.lastMousePoint.X / this.Width;
            //float lastScaledY = this.lastMousePoint.Y / this.Height;
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
                return;
            RotateScene( this.lastMousePoint.X, this.lastMousePoint.Y, e.Location.X, e.Location.Y, this.Width / 3 );
            this.lastMousePoint = e.Location;
            this.Refresh();
        }

        public void Release()
        {
            try
            {
                DeleteSimulation();
            }
            catch (AccessViolationException)
            {
                MessageBox.Show( "some memory could not be released" );
            }
        }

        public void ParsePathFile(String fName,GetStatesProcessProgress progressFuncNotify,GetErrorNotify errorFuncNotify )
        {
            LoadPathFile( fName,progressFuncNotify,errorFuncNotify );
        }

        public void StartMillingAnimation(GetStatesProcessProgress progressNotifyFunc,GetErrorNotify errorNotifyFunc)
        {
            PerformAnimationStep(progressNotifyFunc,errorNotifyFunc);
            this.Refresh();
        }

        public void Reduce()
        {
            SimulatorControlGL.ZoomIn();
            this.Refresh();
        }

        public void Magnify()
        {
            SimulatorControlGL.ZoomOut();
            this.Refresh();
        }

        private void SimulatorControlGL_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.A:
                    TranslateLeft();
                    break;
                case Keys.D:
                    TranslateRight();
                    break;
                case Keys.W:
                    TranslateUp();
                    break;
                case Keys.S:
                    TranslateDown();
                    break;
            }
            this.Refresh();
        }
    }
}
